﻿namespace EyeOpen.BomberMan
{
	using System;
	using System.Drawing;

	public class GameEnvironment
	{
		private readonly Action<Action> temporizedAction;
		private int currentX;
		private int currentY;

		public GameEnvironment(Map map)
		{
			new MapValidator().Validate(map);
			Map = map;

			var position = Map.GetPlayerPosition();
			currentX = position.X - 1;
			currentY = position.Y - 1;
		}

		public GameEnvironment(
			Map map,
			Action<Action> temporizedAction)
			: this(map)
		{
			this.temporizedAction = temporizedAction;
		}

		public Map Map { get; private set; }

		public bool Action(ActionType action)
		{
			var nextX = currentX;
			var nextY = currentY;

			var position = Move(action, nextY, nextX);

			nextX = position.X;
			nextY = position.Y;

			if (action == ActionType.PlaceBomb)
			{
				PlaceBomb();
				return true;
			}

			if (!CanMove(nextY, nextX))
			{
				return false;
			}

			AdjustMap(nextY, nextX);

			return true;
		}

		private void PlaceBomb()
		{
			Map[currentX, currentY] = ItemType.Bomb;

			var clearX = currentX;
			var clearY = currentY;

			Action setWall = () => Map[clearX, clearY] = ItemType.Wall;

			if (temporizedAction == null)
			{
				setWall();
			}
			else
			{
				temporizedAction(setWall);
			}
		}

		private bool CanMove(int x, int y)
		{
			if ((y < 0) || (y >= Map.Size.Width))
			{
				return false;
			}

			if ((x < 0) || (x >= Map.Size.Height))
			{
				return false;
			}

			if (Map[y, x] != ItemType.Wall)
			{
				return false;
			}

			return true;
		}

		private void AdjustMap(int nextY, int nextX)
		{
			if (Map[currentX, currentY] != ItemType.Bomb)
			{
				Map[currentX, currentY] = ItemType.Wall;
			}

			currentX = nextX;
			currentY = nextY;
			Map[currentX, currentY] = ItemType.Player;
		}

		private Point Move(ActionType action, int nextY, int nextX)
		{
			switch (action)
			{
				case ActionType.Up:
					nextY--;
					break;

				case ActionType.Left:
					nextX--;
					break;

				case ActionType.Right:
					nextX++;
					break;

				case ActionType.Down:
					nextY++;
					break;
			}

			return new Point(nextX, nextY);
		}
	}
}